package com.aviator.detective.model.ruleset;

import com.aviator.detective.model.ExecuteElement;
import com.aviator.detective.model.rule.Rule;
import com.aviator.detective.model.SourceCell;
import com.aviator.detective.model.SourceType;
import com.aviator.detective.model.variable.ExpressVariable;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;

import java.util.*;

/**
 * 规则集模型
 */
public class RuleSet implements SourceCell, ExecuteElement {

    private static final Integer DEFAULT_PRIORITY = 0;
    private String id;
    private String alias;
    private String name;
    private RuleExecuteMode ruleExecuteMode = RuleExecuteMode.ALL;
    private Multimap<Integer,Rule> priorityRuleListMap = ArrayListMultimap.create();
    private List<ExpressVariable> variableList = new ArrayList<>();

    public Multimap<Integer,Rule> getPriorityRuleMap(){
        return ImmutableMultimap.copyOf(priorityRuleListMap);
    }

    public List<Map.Entry<Integer, Rule>> ruleSort(){
        List<Map.Entry<Integer, Rule>> entries = new ArrayList<Map.Entry<Integer, Rule>>(priorityRuleListMap.entries());
        Collections.sort(entries, new Comparator<Map.Entry<Integer, Rule>>() {
            @Override
            public int compare(Map.Entry<Integer, Rule> e1, Map.Entry<Integer, Rule> e2) {
                return e1.getKey() - e2.getKey();
            }
        });
        return entries;
    }

    public void addRule(Rule rule){
        addRule(rule,DEFAULT_PRIORITY);
    }

    public void addRule(Rule rule,Integer priority){
        priorityRuleListMap.put(priority,rule);
    }

    public List<ExpressVariable> getVariableList() {
        return Collections.unmodifiableList(variableList);
    }

    public void addExpressVariable(ExpressVariable expressVariable) {
        variableList.add(expressVariable);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void getName(String name) {
        this.name = name;
    }
    @Override
    public String getExecuteElementName() {
        return this.name;
    }

    @Override
    public SourceType getType() {
        return SourceType.RULESET;
    }

    /**
     * 规则集的执行有三种模式
     * PRIORITY_CONTINUE：按优先级执行
     * PRIORITY_INTERRUPT：按优先级执行，前面结果为false，后面不执行
     * ALL：随机执行
     */
    enum RuleExecuteMode{
        PRIORITY_CONTINUE,PRIORITY_INTERRUPT,ALL
    }

    @Override
    public String toAviatorScript() {
        Preconditions.checkNotNull(priorityRuleListMap,"priorityRuleListMap not be null");
        Preconditions.checkArgument(priorityRuleListMap.size()==0,"priorityRuleListMap not be empty");
        StringBuffer aviatorScript = new StringBuffer();
        switch (ruleExecuteMode){
            case ALL:
                return toAviatorScriptByAll(aviatorScript);
            case PRIORITY_CONTINUE:
                return toAviatorScriptByPriorityContinue(aviatorScript);
            case PRIORITY_INTERRUPT:
                return toAviatorScriptByPriorityInterrupt(aviatorScript);
            default:
                throw new IllegalArgumentException("illegal rule execute mode");
        }
    }

    /**
     *if(rule1){
     *     variable1;
     *     variable2;
     *}
     * if(rule2){
     *     variable3;
     *     variable4;
     * }
     * @return
     */
    public String toAviatorScriptByPriorityContinue(StringBuffer aviatorScript){
        List<Map.Entry<Integer, Rule>> entryList = ruleSort();
        for (Map.Entry<Integer, Rule> integerRuleEntry : entryList) {
            Rule rule = integerRuleEntry.getValue();
            String ruleScript = rule.toAviatorScript();
            aviatorScript.append(ruleScript).append("\\n");
        }
        return aviatorScript.toString();
    }
    /**
     *if(rule1){
     *     variable1;
     *     variable2;
     *     if(rule2){
     *          variable3;
     *          variable4;
     *             if(rule3){
     *                variable5;
     *                variable6;
     *            }
     *      }
     *}
     * @return
     */
    public String toAviatorScriptByPriorityInterrupt(StringBuffer aviatorScript){
        List<Map.Entry<Integer, Rule>> entryList = ruleSort();
        int depth = 0;
        for (Map.Entry<Integer, Rule> integerRuleEntry : entryList) {
            depth ++;
            Rule rule = integerRuleEntry.getValue();
            String lhsScript = rule.toAviatorScript();
            aviatorScript.append(lhsScript);
            String rhsScript = rule.rhs();
            aviatorScript.append(rhsScript.trim(), 0, rhsScript.length()-1);
        }
        while (depth > 0){
            depth--;
            aviatorScript.append(RIGHT_BRACE);
            aviatorScript.append("\\n");
        }
        return aviatorScript.toString();
    }
    /**
     *if(rule2){
     *     variable1;
     *     variable2;
     *}
     * if(rule1){
     *     variable3;
     *     variable4;
     * }
     * @return
     */
    public String toAviatorScriptByAll(StringBuffer aviatorScript){
        for (Map.Entry<Integer, Rule> integerRuleEntry : priorityRuleListMap.entries()) {
            Rule rule = integerRuleEntry.getValue();
            String ruleScript = rule.toAviatorScript();
            aviatorScript.append(ruleScript).append("\\n");
        }
        return aviatorScript.toString();
    }
}
